/*
 * @File:   lua2rtt.c
 * @Author: liu2guang
 * @Date:   2018-05-06 09:16:56
 *
 * @LICENSE: https://github.com/liu2guang/lua2rtt/blob/master/LICENSE.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-05-06     liu2guang    The first version.
 * 2018-07-03     liu2guang    Update lua5.1.2->lua5.3.4.
 */

#include "lua2rtt.h"
#include <devices/uart.h>
#include <aos/console_uart.h>

static struct lua2rtt handle = {0};

extern int32_t cli_printf(const char *fmt, ...);

static unsigned char lua2rtt_getc(void)
{
    unsigned char ch = 0;

    while (rvm_hal_uart_recv(console_get_uart(), &ch, 1, AOS_WAIT_FOREVER) != 1);

    return ch;
}

static int lua2rtt_handle_history(const char *prompt)
{
    cli_printf("\033[2K\r");
    cli_printf("%s%s", prompt, handle.line);
    return 0;
}

static void lua2rtt_push_history(void)
{
    if (handle.line_position > 0)
    {
        if (handle.history_count >= LUA2RTT_HISTORY_LINES)
        {
            if (memcmp(&handle.lua_history[LUA2RTT_HISTORY_LINES - 1], handle.line, LUA2RTT_CMD_SIZE))
            {
                int index;
                for (index = 0; index < 4; index++)
                {
                    memcpy(&handle.lua_history[index][0], &handle.lua_history[index + 1][0], LUA2RTT_CMD_SIZE);
                }
                memset(&handle.lua_history[index][0], 0, LUA2RTT_CMD_SIZE);
                memcpy(&handle.lua_history[index][0], handle.line, handle.line_position);

                handle.history_count = LUA2RTT_HISTORY_LINES;
            }
        }
        else
        {
            if (handle.history_count == 0 || memcmp(&handle.lua_history[handle.history_count - 1], handle.line, LUA2RTT_CMD_SIZE))
            {
                handle.history_current = handle.history_count;
                memset(&handle.lua_history[handle.history_count][0], 0, LUA2RTT_CMD_SIZE);
                memcpy(&handle.lua_history[handle.history_count][0], handle.line, handle.line_position);

                handle.history_count++;
            }
        }
    }

    handle.history_current = handle.history_count;
}

int lua2rtt_readline(const char *prompt, char *buffer, int buffer_size)
{
    unsigned char ch;

start:
    cli_printf(prompt);

    while (1)
    {
        ch = lua2rtt_getc();

        if (ch == 0x1b)
        {
            handle.stat = LUA2RTT_WAIT_SPEC_KEY;
            continue;
        }
        else if (handle.stat == LUA2RTT_WAIT_SPEC_KEY)
        {
            if (ch == 0x5b)
            {
                handle.stat = LUA2RTT_WAIT_FUNC_KEY;
                continue;
            }
            handle.stat = LUA2RTT_WAIT_NORMAL;
        }
        else if (handle.stat == LUA2RTT_WAIT_FUNC_KEY)
        {
            handle.stat = LUA2RTT_WAIT_NORMAL;

            if (ch == 0x41)
            {
                if (handle.history_current > 0)
                {
                    handle.history_current--;
                }
                else
                {
                    handle.history_current = 0;
                    continue;
                }

                memcpy(handle.line, &handle.lua_history[handle.history_current][0], LUA2RTT_CMD_SIZE);
                handle.line_curpos = handle.line_position = strlen(handle.line);
                lua2rtt_handle_history(prompt);

                continue;
            }
            else if (ch == 0x42)
            {
                if (handle.history_current < (handle.history_count - 1))
                {
                    handle.history_current++;
                }
                else
                {
                    if (handle.history_count != 0)
                    {
                        handle.history_current = handle.history_count - 1;
                    }
                    else
                    {
                        continue;
                    }
                }

                memcpy(handle.line, &handle.lua_history[handle.history_current][0], LUA2RTT_CMD_SIZE);
                handle.line_curpos = handle.line_position = strlen(handle.line);
                lua2rtt_handle_history(prompt);

                continue;
            }
            else if (ch == 0x44)
            {
                if (handle.line_curpos)
                {
                    cli_printf("\b");
                    handle.line_curpos--;
                }
                continue;
            }
            else if (ch == 0x43)
            {
                if (handle.line_curpos < handle.line_position)
                {
                    cli_printf("%c", handle.line[handle.line_curpos]);
                    handle.line_curpos++;
                }
                continue;
            }
        }

        if (ch == '\0' || ch == 0xFF)
        {
            continue;
        }

        else if (ch == 0x7f || ch == 0x08)
        {
            if (handle.line_curpos == 0)
            {
                continue;
            }

            handle.line_curpos--;
            handle.line_position--;

            if (handle.line_position > handle.line_curpos)
            {
                memmove(&handle.line[handle.line_curpos], &handle.line[handle.line_curpos + 1],
                        handle.line_position - handle.line_curpos);
                handle.line[handle.line_position] = 0;

                cli_printf("\b%s  \b", &handle.line[handle.line_curpos]);

                int index;
                for (index = (handle.line_curpos); index <= (handle.line_position); index++)
                {
                    cli_printf("\b");
                }
            }
            else
            {
                cli_printf("\b \b");
                handle.line[handle.line_position] = 0;
            }
            continue;
        }

        else if (ch == '\r' || ch == '\n')
        {
            lua2rtt_push_history();

            cli_printf("\r\n");
            if (handle.line_position == 0)
            {
                goto start;
            }
            else
            {
                unsigned char temp = handle.line_position;

                strncpy(buffer, handle.line, handle.line_position);
                memset(handle.line, 0x00, sizeof(handle.line));
                buffer[handle.line_position] = 0;
                handle.line_curpos = handle.line_position = 0;
                return temp;
            }
        }

        else if (ch == 0x04)
        {
            if (handle.line_position == 0)
            {
                return 0;
            }
            else
            {
                continue;
            }
        }

        else if (ch == '\t')
        {
            continue;
        }

        else if (ch < 0x20 || ch >= 0x80)
        {
            continue;
        }

        if (handle.line_position >= LUA2RTT_CMD_SIZE)
        {
            continue;
        }

        if (handle.line_curpos < handle.line_position)
        {
            memmove(&handle.line[handle.line_curpos + 1], &handle.line[handle.line_curpos],
                    handle.line_position - handle.line_curpos);
            handle.line[handle.line_curpos] = ch;

            cli_printf("%s", &handle.line[handle.line_curpos]);

            int index;
            for (index = (handle.line_curpos); index < (handle.line_position); index++)
            {
                cli_printf("\b");
            }
        }
        else
        {
            handle.line[handle.line_position] = ch;
            cli_printf("%c", ch);
        }

        ch = 0;
        handle.line_curpos++;
        handle.line_position++;
    }
}

static void lua2rtt_run(void *p)
{
    if (handle.argc == 1)
    {
        cli_printf("\r\nPress CTRL+D to exit Lua.\r\n");
    }
    extern int lua_main(int argc, char **argv);
    lua_main(handle.argc, handle.argv);

    if (handle.argc > 1)
    {
        free(handle.argv[1]);
    }
}

static int lua2rtt(int argc, char **argv)
{
    static int history_init = 0;

    if (history_init == 0)
    {
        memset(&handle, 0x00, sizeof(struct lua2rtt));
        history_init = 1;
    }
    else
    {
        handle.thread = NULL;
        handle.stat = LUA2RTT_WAIT_NORMAL;
        handle.argc = 0;
        handle.argv[0] = NULL;
        handle.argv[1] = NULL;
        handle.argv[2] = NULL;
        memset(handle.line, 0x00, LUA2RTT_CMD_SIZE);
        handle.line_position = 0;
        handle.line_curpos = 0;
    }

    handle.argc = argc;

    handle.argv[0] = "lua";
    handle.argv[1] = NULL;
    handle.argv[2] = NULL;

    if (argc > 1)
    {
        unsigned long len = strlen(argv[1]);
        handle.argv[1] = (char *)malloc(len + 1);
        memset(handle.argv[1], 0x00, len + 1);
        strncpy(handle.argv[1], argv[1], len);
    }

    lua2rtt_run(NULL);

    return 0;
}
ALIOS_CLI_CMD_REGISTER(lua2rtt, lua, Execute Lua parser.);
